import select
import threading
from abc import ABCMeta, abstractmethod

INDICATOR_CHAR = "!"


class BaseSelectable(object):
    """
    A base expectable class from which other selectables are derived.
    
    This class can also be used as a lock to lock data related to it.
    """
    __metaclass__ = ABCMeta
    
    def __init__(self):
        self._lock = threading.RLock()
    
    def __enter__(self):
        self._lock.acquire()
    
    def __exit__(self, type, value, traceback):
        self._lock.release()
    
    def wait(self, timeout=None):
        if timeout is None:
            readable, _writable, _other = select.select([self], [], [])
        else:
            readable, _writable, _other = select.select([self], [], [], timeout)
        return self in readable
    
    @abstractmethod
    def fileno(self):
        """
        Return an os-level file descriptor that can be used with select(),
        to work on Windows, this should be a socket file descriptor.
        """
        raise NotImplementedError # pragma: no cover
        
    @abstractmethod
    def set(self):
        """
        Set the Selectable so that it is readable to select().
        """
        raise NotImplementedError # pragma: no cover
        
    def is_set(self):
        """
        Return True if the Selectable is set.
        """
        raise NotImplementedError # pragma: no cover
        
    @abstractmethod
    def clear(self):
        """
        Clear the selectable object so that it is no longer readable to select().
        """
        raise NotImplementedError # pragma: no cover